__doc__ = '''
A parser for a subset of x4gl syntax.
'''
__copyright__ = '(c)2007 Mark J. Rees http://jmmjsolutions.com'
__license__ = ''' GNU General Public License

This program is free software; 
you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA. '''

import x4gllexer
import ply.yacc as yacc
import sys

trace = 0
def traceit(*msgs):
    for msg in msgs:
        print msg
        
def error(*msgs):
    for msg in msgs:
        sys.stderr.write(msg + "\n")
    
tokens = x4gllexer.tokens

class DataType(object):
    def __init__(self, datatype, value=None):
        self.datatype = datatype
        self.value = value

    def __add__(self, other):
        other = self._convert_other(other)
        if other is NotImplemented:
            return other
        return DataType(self.datatype, self.value + other.value)

    def __sub__(self, other):
        other = self._convert_other(other)
        if other is NotImplemented:
            return other
        return DataType(self.datatype, self.value - other.value)

    def __mul__(self, other):
        other = self._convert_other(other)
        if other is NotImplemented:
            return other
        return DataType(self.datatype, self.value * other.value)

    def __div__(self, other):
        other = self._convert_other(other)
        if other is NotImplemented:
            return other
        return DataType(self.datatype, self.value / other.value)

    def _convert_other(self, other):
        '''Convert other to DataType.'''
        if isinstance(other, DataType):
            return other
        if isinstance(other, (int, long)):
            return DataType('INTEGER', other)
        return NotImplemented
        
variables = {}

precedence = (
    ('left','PLUS','MINUS'),
    ('left','TIMES','DIVIDE'),
    ('right','UMINUS'),
    )
    
def p_statement_define(t):
    'statement : DEFINE IDENTIFIER'
    varname = t[2]
    if not variables.has_key(varname):
        variables[varname] = DataType('VARIANT', None)
    else:
        error("Variable %s already defined" % varname)
        
def p_statement_assign(t):
    '''statement : IDENTIFIER ASSIGN expression
                 | LET IDENTIFIER ASSIGN expression'''
    if len(t) == 5:
        varname = t[2]
        value = t[4]
    else:
        varname = t[1]
        value = t[3]
    if variables.has_key(varname):
        variables[varname].value = value
    else:
        error('Variable %s as not be defined' % varname)
    
def p_statement_expr(t):
    'statement : expression'
    traceit(t[1].value)
    
def p_expression_binop(t):
    '''expression : expression PLUS expression
                  | expression MINUS expression
                  | expression TIMES expression
                  | expression DIVIDE expression'''
    if t[2] == '+' : t[0] = t[1] + t[3]
    elif t[2] == '-': t[0] = t[1] - t[3]
    elif t[2] == '*': t[0] = t[1] * t[3]
    elif t[2] == '/': t[0] = t[1] / t[3]

def p_expression_uminus(t):
    'expression : MINUS expression %prec UMINUS'
    t[0] = -t[2]

def p_expression_group(t):
    'expression : LPAREN expression RPAREN'
    t[0] = t[2]

def p_expression_number(t):
    'expression : NUMBER'
    t[0] = t[1]
    
def p_expression_string(t):
    'expression : LSTRING'
    #FIXME: Need to remove escaping of quotes
    t[0] = t[1][1:-1]   # Strip quotes

def p_expression_name(t):
    'expression : IDENTIFIER'
    try:
        t[0] = variables[t[1]]
    except LookupError:
        error("Undefined name '%s'" % t[1])
        t[0] = 0

def p_error(t):
    error("Syntax error at '%s'" % t.value)
    
yacc.yacc()

if __name__ == '__main__':
    trace = 1
    while 1:
        try:
            s = raw_input('x4gl>> ').lower()
        except EOFError:
            break
        yacc.parse(s)
